﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Text;

#if DEBUG

using NUnit.Core;

namespace STCommon.NUnit
{
    public class NUnitTestManager : INUnitTestManager
    {
        private NUnitTestManager()
        {
            CoreExtensions.Host.InitializeService();
        }

#region INUnitTestManager Members

        public void RegisterTestAssembly(string assemblyPath)
        {
            FileStream assemblyFile;

            try
            {
                assemblyFile = File.OpenRead(assemblyPath);
            }
            catch
            {
                // Can't open file, ignore assembly
                return;
            }

            if (assemblyFile != null &&
                !m_RegisteredAssemblies.Contains(assemblyFile.Name))
            {
                m_RegisteredAssemblies.Add(assemblyFile.Name);

                if (TestAssemblyRegistered != null)
                {
                    TestAssemblyRegistered(this, new PropertyChangedEventArgs("TestAssemblies"));
                }

                assemblyFile.Close();
            }
        }

        public void RunTests(EventListener listener)
        {
            RunTests(m_RegisteredAssemblies, listener);
        }

        public void RunTests(string assembly, EventListener listener)
        {
            List<string> testAssembly = new List<string>();

            testAssembly.Add(assembly);
            RunTests(testAssembly, listener);
        }

        public void RunTests(List<string> assemblies, EventListener listener)
        {
            TestPackage preconditionsTestPackage = new TestPackage("Preconditions Tests", assemblies);
            TestPackage testPackage = new TestPackage("Unit Tests", assemblies);
            SimpleTestRunner runner = new SimpleTestRunner();
            EventListener eventListener = listener;

            if (eventListener == null)
            {
                eventListener = new NullListener();
            }

            // Run the preconditions first
            if (runner.Load(preconditionsTestPackage))
            {
                ITestFilter preconditionsFilter = new NUnitPreconditionsFilter();

                if (runner.CountTestCases(preconditionsFilter) > 0)
                {
                    runner.BeginRun(eventListener, preconditionsFilter);
                }
            }

            // If preconditions pass (or we have none), run the tests
            if (runner.TestResult == null ||
                !runner.TestResult.HasResults ||
                runner.TestResult.IsSuccess)
            {
                runner.Unload();

                if (runner.Load(testPackage))
                {
                    runner.BeginRun(eventListener, new NUnitNotPreconditionsFilter());
                }
            }
        }

        public IList<string> TestAssemblies
        {
            get { return m_RegisteredAssemblies.AsReadOnly(); }
        }

        public event PropertyChangedEventHandler TestAssemblyRegistered;

#endregion

        public void ClearAssemblies()
        {
            if(m_RegisteredAssemblies.Count > 0)
            {
                m_RegisteredAssemblies.Clear();

                if (TestAssemblyRegistered != null)
                {
                    TestAssemblyRegistered(this, new PropertyChangedEventArgs("TestAssemblies"));
                }
            }
        }

        private static INUnitTestManager FindManagerInstance()
        {
            INUnitTestManager result = null;

            // Check in all loaded assemblies for the owner of the manager
            foreach (Assembly loadedAssembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                // Don't look into ourselves
                if (loadedAssembly != typeof(NUnitTestManager).Assembly)
                {
                    Type testManagerType = loadedAssembly.GetType("STCommon.NUnit.NUnitTestManager");

                    if (testManagerType != null)
                    {
                        PropertyInfo localInstanceProperty = testManagerType.GetProperty("LocalInstance");

                        if (localInstanceProperty != null && localInstanceProperty.CanRead)
                        {
                            try
                            {
                                object localInstance = localInstanceProperty.GetGetMethod().Invoke(null, null);

                                if (localInstance != null)
                                {
                                    return new NUnitTestManagerProxy(localInstance);
                                }

                                return null;
                            }
                            catch
                            {
                                return null;
                            }
                        }
                    }
                }
            }

            return result;
        }


        public static INUnitTestManager LocalInstance
        {
            get
            {
                return m_LocalInstance;
            }
        }

        public static INUnitTestManager Instance
        {
            get
            {
                if (m_Instance == null)
                {
                    INUnitTestManager loadedManager = FindManagerInstance();

                    if (loadedManager == null)
                    {
                        m_LocalInstance = new NUnitTestManager();
                        loadedManager = m_LocalInstance;
                    }

                    m_Instance = loadedManager;
                }

                return m_Instance;
            }
        }

        private static INUnitTestManager m_LocalInstance = null;
        private static INUnitTestManager m_Instance = null;

        private List<String> m_RegisteredAssemblies = new List<String>();
    }
}

#endif
